package com.wzw.basics.concurrent.jmm;

import java.util.concurrent.TimeUnit;

/**
 * Java内存模型
 * Java内存模型中规定所有变量都存储在主内存，主内存是共享内存区域，所有线程都可以访问，但线程对变量的操作（读取赋值等）必须在工作内存中进行，
 * 首先要将变量从主内存拷贝的自己的工作内存空间，然后对变量进行操作，操作完成后再将变量写回主内存，不能直接操作主内存中的变量，
 * 各个线程中的工作内存中存储着主内存中的变量副本拷贝，因此不同的线程间无法访问对方的工作内存，线程间的通信（传值）必须通过主内存来完成
 * - 线程加锁前必须读取主内存的最新值到自己的本地内存存（栈空间）
 * - 线程解锁前必须把本地内存中的共享变量值数显到主内存中
 * - 线程加锁和解锁要是同一把锁
 *
 * @author Wangzhiwen
 */
public class JMM {

    /**
     * 可见性说明，多个操作同一个对象visibility内的属性值。
     * 线程复制主内存中的变量值到自己的工作内存，修改完毕后刷新到主内存。其他线程会感知到主内存中的变量发生变化，而重新复制一份到自己的工作内存。
     */
    public static synchronized void visibility(visibility visibility) {
        new Thread(() -> {
            // 线程进入标识
            System.out.println(Thread.currentThread().getName() + "\t" + "come in");
            // 先让线程休眠，假设在进行业务运算
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                //ignore
            }
            // 修改对象变量的值
            visibility.setNumber(999);
            // 输出结果
            System.out.println(Thread.currentThread().getName() + "\t" + "update number the value : " + visibility.getNumber());
        }, "AAA").start();
    }

    public static void main(String[] args) {
        // 同一个对象
        visibility visibility = new visibility();
        // 异步操作对象内的属性，但是这个异步的线程处理的结果比较慢。可能在主线程执行完成之后，还未执行完成。简单来说就是对象属性还没有被修改前主线程就已经执行完了
        visibility(visibility);
        // main 对象的属性被修改过才会停止循环
//        for (;;){
//            if (visibility.getNumber() != 0){
//                System.out.println("aa");
//            }
//        }
//        Date init = new Date();
        while (visibility.getNumber() == 0){
            // 输出方法是加锁的方法，加锁时线程必须主动重新去直接内存中复制变量到本地内存中，解锁前也必须把值刷新回主内存中。
            //System.out.println(Thread.currentThread().getName() + "\t" + (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()-init.getTime())));
        }
//        try {
//            TimeUnit.SECONDS.sleep(4);
//        } catch (InterruptedException e) {
//            // ignore
//        }

        // 按道理这个值是不可能打印出来的，因为主线程运行的时候，number的值为0，所以一直在循环
        // 如果能输出这句话，说明AAA线程在睡眠3秒后，更新的number的值，重新写入到主内存，并被main线程感知到了
        System.out.println(Thread.currentThread().getName() + "\t" + "mission is over");

    }
}

class visibility {
    // 由于没有volatile修饰MyData类的成员变量number，main线程将会卡在while(myData.number == 0) {}，不能正常结束。
    // 若想正确结束，用volatile修饰MyData类的成员变量number吧。
    private int number;
    //private volatile int number;

    public void setNumber(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }
}
